﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Phoenix.Xna.Framework.Reflection;
using Phoenix.Xna.Framework.Graphics.Models;

namespace Phoenix.Xna.Framework.ContentPipeline
{
    /// <summary>
    /// A helper class to load and unload assets through the content pipeline with a specific IContentService.
    /// </summary>
    public class ContentHelper
    {
        public static IContentService _contentService;      // The content service to use to load/unload content.

        /// <summary>
        /// Gets or sets the list of Types that can be loaded through the content pipeline.
        /// </summary>
        public static List<Type> LoadableTypes { get; private set; }

        /// <summary>
        /// Gets or sets the content service used to load/unload content.
        /// </summary>
        public static IContentService Content
        {
            get
            {
                if (_contentService == null)
                {
                    _contentService = Engine.Instance.Content;
                }

                return _contentService;
            }
            set
            {
                if (value == null)
                {
                    _contentService = Engine.Instance.Content;
                }
                else
                {
                    _contentService = value;
                }
            }
        }

        static ContentHelper()
        {
            InitializeLoadableTypes();
        }

        /// <summary>
        /// Register a custom type that is loadable through the content pipeline.
        /// </summary>
        /// <param name="type">The type to register.</param>
        public void RegisterLoadableType(Type type)
        {
            if (!LoadableTypes.Contains(type))
            {
                LoadableTypes.Add(type);
            }
        }

        /// <summary>
        /// Determines if the specified type is loadable through the content pipeline.
        /// </summary>
        /// <param name="type">The Type to check.</param>
        /// <returns>True if loadable, otherwise false.</returns>
        public static bool IsLoadableType(Type type)
        {
            return LoadableTypes.Contains(type);
        }

        /// <summary>
        /// Determines if the specified type is loadable through the content pipeline.
        /// </summary>
        /// <typeparam name="T">The Type to check.</typeparam>
        /// <returns>True if loadable, otherwise false.</returns>
        public static bool IsLoadableType<T>()
        {
            return IsLoadableType(typeof(T));
        }

        /// <summary>
        /// Loads the specified asset as the specified type.
        /// </summary>
        /// <param name="type">The Type of the asset being loaded.</param>
        /// <param name="asset">The asset to load.</param>
        /// <returns>The loaded asset as an object.</returns>
        public static object Load(Type type, string asset)
        {
            if (IsLoadableType(type))
            {
                if (!type.IsArray)
                {
                    GenericInvoker invoker;
                    invoker = ReflectionHelper.GenericMethodInvokerMethod(typeof(ContentService), "Load", new Type[] { type });
                    return invoker(Content, asset);
                }
                else
                {
                    // TODO : Get basetype of array and load all assets.
                }
            }

            return null;
        }

        /// <summary>
        /// Loads the specified asset of type T.
        /// </summary>
        /// <typeparam name="T">The Type of the asset being loaded.</typeparam>
        /// <param name="asset">The asset to load.</param>
        /// <returns>The loaded asset as Type T.</returns>
        public static T Load<T>(string asset)
        {
            return (T)Load(typeof(T), asset);
        }

        /// <summary>
        /// Unload the specified asset.
        /// </summary>
        /// <param name="asset">The asset to unload.</param>
        public static void Unload(string asset)
        {
            string[] assets = asset.Split(',');

            for (int i = 0; i < assets.Length; i++)
            {
                Content.Release(assets[i]);
            }
        }

        private static void InitializeLoadableTypes()
        {
            LoadableTypes = new List<Type>();
            LoadableTypes.Add(typeof(Texture));
            LoadableTypes.Add(typeof(Texture[]));
            LoadableTypes.Add(typeof(Texture2D));
            LoadableTypes.Add(typeof(Texture2D[]));
            LoadableTypes.Add(typeof(Model));
            LoadableTypes.Add(typeof(Model[]));
            LoadableTypes.Add(typeof(InstancedModel));
            LoadableTypes.Add(typeof(InstancedModel[]));
            LoadableTypes.Add(typeof(SkinnedModel));
            LoadableTypes.Add(typeof(SkinnedModel[]));
        }

        private static List<T> LoadArray<T>(string asset)
        {
            string[] assets = asset.Split(',');
            List<T> data = new List<T>();

            for (int i = 0; i < assets.Length; i++)
            {
                data.Add(Content.Load<T>(assets[i]));
            }

            return data;
        }
    }
}
